/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */

package tools;

import com.openbrew.openbrewrf.POSLineItems;
import com.openbrew.openbrewrf.POSSale;
import com.openbrew.openbrewrf.POSSetup;
import com.openbrew.openbrewrf.POSTasterTray;
import com.openbrew.openbrewrf.PosLoyaltyCard;
import com.openbrew.openbrewrf.PosLoyaltyPoints;
import java.util.ArrayList;
import java.util.List;
import javax.persistence.EntityManager;
import javax.persistence.TypedQuery;

/**
 *
 * @author chapmand
 */
public class POSSaleTools {
    /***************************************************************************
     * POS Sale Update Tools.
     **************************************************************************/
    /***************************************************************************
     * Insert a POS Sale based on arrays sent.
     * @param en - Entity Manager
     * @param posSale - POSSale being inserted.
     * @return POSSale updated POSSale
     **************************************************************************/
    public POSSale doPOSSaleInsertActions(final EntityManager en
            , final POSSale posSale){
        if(posSale.getLineItemTypeCollection()!=null){
            List<POSLineItems> lineItems = new ArrayList<POSLineItems>();
            for(POSLineItems line : posSale.getLineItemTypeCollection()){
                if(line.getTasterTray()!=null){
                    POSTasterTray item = createTasterTray(en, line.getTasterTray());
                    line.setTasterTray(item);
                }
                //Insert line item and addit back to the list.
                lineItems.add(createLineItem(en, line));
                //Fix inventories accordingly.
                doLineItemInventoryMods(en, line);
            }
            posSale.setLineItemTypeCollection(lineItems);
        }
        return posSale;
    }
    
    /***************************************************************************
     * Do line item inventory updates
     **************************************************************************/
    private void doLineItemInventoryMods(final EntityManager en, POSLineItems line){
        if(line.getSupply()!=null){
            InventoryModifiers im = new InventoryModifiers();
            im.doSupplyRemovalWithoutAllocation(en
                    , line.getSupply().getSupID(), (float)line.getQty());
        }
        if(line.getProduct()!=null){
            POSVolumeModifier vm = new POSVolumeModifier();
            vm.doVolumeRemoval(en, line);
        }
        if(line.getTasterTray()!=null){
            POSVolumeModifier vm = new POSVolumeModifier();
            vm.doVolumeRemoval(en, line.getTasterTray().getServingTypeCollection());
        }
    }
    
    /***************************************************************************
     * Create a taster tray.
     **************************************************************************/
    private POSTasterTray createTasterTray(final EntityManager en, POSTasterTray entity){
        en.persist(entity);
        en.flush();
        en.refresh(entity);
        return entity;
    }
    
    /***************************************************************************
     * Create a Supply Item.
     **************************************************************************/
    private POSLineItems createLineItem(final EntityManager en, POSLineItems entity){
        en.persist(entity);
        en.flush();
        en.refresh(entity);
        return entity;
    }
    
    /***************************************************************************
     * POS Loyalty Card Tools
     **************************************************************************/
    /***************************************************************************
     * Insert a POS Sale based on arrays sent.
     * @param en - Entity Manager
     * @param posSale - POSSale being inserted / updated.
     * @return updated POSSale
     **************************************************************************/
    public POSSale doLoyaltyItemsOnUpdate(final EntityManager en
            , final POSSale posSale){
        if(posSale.getLoyaltyCard()!=null){
            //Loyalty Card is set, run this tool.
            final POSSale oldSale = getPOSSaleFromID(en, posSale.getId());
            if(!oldSale.isSaleComplete() && posSale.isSaleComplete()){
                //Sale was just set to complete, continue.
                final POSSale updatedSale = doPointsAddition(en, posSale);
                return updatedSale;
            } else if(oldSale.isSaleComplete() && !posSale.isSaleComplete()){
                //Salewas just reversed, remove credited points.
                final POSSale updatedSale = doPointsSubtraction(en, posSale);
                return updatedSale;
            }
        }
        return posSale;
    }
    /***************************************************************************
     * Insert a POS Sale based on arrays sent.
     * @param en - Entity Manager
     * @param posSale - POSSale being inserted / updated.
     * @return updated POSSale
     **************************************************************************/
    public POSSale doLoyaltyItemsOnInsert(final EntityManager en
            , final POSSale posSale){
        if(posSale.getLoyaltyCard()!=null){
            //Loyalty Card is set, run this tool.
            if(posSale.isSaleComplete()){
                //Sale was just set to complete, continue.
                final POSSale updatedSale = doPointsAddition(en, posSale);
                return updatedSale;
            } 
        }
        return posSale;
    }
    
    private POSSale doPointsAddition(final EntityManager en
            , final POSSale posSale){
        int pointsToAdd = 0;
        int growlerPointsToAdd = 0;
        int freeGrowlerNumToRemove = 0;
        int freeGrowlerNumToAdd = 0;
        float saleMoney = 0;
        final POSSetup setup = getPOSSetup(en);
        if(posSale.getLineItemTypeCollection()!=null){
            for(POSLineItems lineItem : posSale.getLineItemTypeCollection()){
                if(lineItem.getProduct()!=null && lineItem.getDiscount()==null){
                    if(lineItem.getProduct().getGlassware().isLoyaltyPoints()){
                        pointsToAdd = pointsToAdd + lineItem.getQty();
                    }
                    if(lineItem.getProduct().getGlassware().isLoyaltyGrowler()){
                        growlerPointsToAdd = growlerPointsToAdd + lineItem.getQty();
                    }
                } else if(lineItem.getProduct()!=null 
                        && setup.getGrowlerDiscount()!=null
                        && lineItem.getDiscount()!=null 
                        && lineItem.getDiscount().getId() == setup.getGrowlerDiscount().getId()){
                    if(lineItem.getProduct().getGlassware().isLoyaltyGrowler()){
                        freeGrowlerNumToRemove = freeGrowlerNumToRemove + lineItem.getQty();
                    }
                }
                //Now calculate dollar amount of eligible sales.
                if(lineItem.getProduct()!=null){
                    if(lineItem.getProduct().getGlassware().isLoyaltyPoints()){
                        float lineItemCost = (float)lineItem.getQty() * lineItem.getProduct().getStCost();
                        if(lineItem.getDiscount()!=null){
                            if(lineItem.getDiscount().getFlat()>0){
                                lineItemCost = lineItemCost 
                                        - ((float)lineItem.getQty() * lineItem.getDiscount().getFlat());
                            } else if(lineItem.getDiscount().getPct()>0){
                                lineItemCost = lineItemCost * 
                                        ((100 - (lineItem.getDiscount().getPct()))/100);
                            }
                        }
                        saleMoney = saleMoney + lineItemCost;
                    }
                } 
            }
        }

        //Parse through points to determine if credit needs to be added.
        float rewardedValue = posSale.getLoyaltyCard().getCurrentBalance();
        List<PosLoyaltyPoints> points = en.createNamedQuery("posLoyaltyPoints.findAll", PosLoyaltyPoints.class).getResultList();
        for(final PosLoyaltyPoints point : points){
            if((posSale.getLoyaltyCard().getEarnedPoints() < point.getLpPoints()) 
                    && (posSale.getLoyaltyCard().getEarnedPoints() + pointsToAdd >= point.getLpPoints())){
                rewardedValue += point.getLpValue();
            }
        }

        //Now update the rewards.
        PosLoyaltyCard card = posSale.getLoyaltyCard();
        card.setEarnedPoints(card.getEarnedPoints() + pointsToAdd);
        int currentCount = card.getGrowlerCount();
        while(currentCount < card.getGrowlerCount() + growlerPointsToAdd){
            currentCount++;
            if((currentCount % setup.getGrowlerCount()) == 0){
                freeGrowlerNumToAdd++;
            }
        }
        card.setGrowlerCount(card.getGrowlerCount() + growlerPointsToAdd);
        int newGrowlerNum = card.getEarnedGrowlers() - freeGrowlerNumToRemove + freeGrowlerNumToAdd;
        if(newGrowlerNum < 0){
            newGrowlerNum = 0;
        }
        card.setEarnedGrowlers(newGrowlerNum);
        saleMoney = saleMoney - posSale.getLoyaltyMoney();
        if(saleMoney < 0){
            saleMoney = 0;    
        }
        float earnedPctMoney = saleMoney * (setup.getValuePerPoint()/100);
        float newBalance = rewardedValue - posSale.getLoyaltyMoney() + earnedPctMoney;
        card.setCurrentBalance(newBalance);
        en.merge(card);
        posSale.setLoyaltyCard(card);
        
        return posSale;
    }
    
    private POSSale doPointsSubtraction(final EntityManager en
            , final POSSale posSale){
        int pointsToRemove = 0;
        int growlerPointsToRemove = 0;
        int freeGrowlerNumToAdd = 0;
        int freeGrowlerNumToRemove = 0;
        float saleMoney = 0;
        final POSSetup setup = getPOSSetup(en);
        if(posSale.getLineItemTypeCollection()!=null){
            for(POSLineItems lineItem : posSale.getLineItemTypeCollection()){
                if(lineItem.getProduct()!=null && lineItem.getDiscount()==null){
                    if(lineItem.getProduct().getGlassware().isLoyaltyPoints()){
                        pointsToRemove = pointsToRemove + lineItem.getQty();
                    }
                    if(lineItem.getProduct().getGlassware().isLoyaltyGrowler()){
                        growlerPointsToRemove = growlerPointsToRemove + lineItem.getQty();
                    }
                } else if(lineItem.getProduct()!=null 
                        && setup.getGrowlerDiscount()!=null
                        && lineItem.getDiscount()!=null 
                        && lineItem.getDiscount().getId() == setup.getGrowlerDiscount().getId()){
                    if(lineItem.getProduct().getGlassware().isLoyaltyGrowler()){
                        freeGrowlerNumToAdd = freeGrowlerNumToAdd + lineItem.getQty();
                    }
                }
                //Now calculate dollar amount of eligible sales.
                if(lineItem.getProduct()!=null){
                    if(lineItem.getProduct().getGlassware().isLoyaltyPoints()){
                        float lineItemCost = (float)lineItem.getQty() * lineItem.getProduct().getStCost();
                        if(lineItem.getDiscount()!=null){
                            if(lineItem.getDiscount().getFlat()>0){
                                lineItemCost = lineItemCost 
                                        - ((float)lineItem.getQty() * lineItem.getDiscount().getFlat());
                            } else if(lineItem.getDiscount().getPct()>0){
                                lineItemCost = lineItemCost * 
                                        ((100 - (lineItem.getDiscount().getPct()))/100);
                            }
                        }
                        saleMoney = saleMoney + lineItemCost;
                    }
                } 
            }
        }

        //Parse through points to determine if credit needs to be removed.
        float rewardedValue = posSale.getLoyaltyCard().getCurrentBalance();
        List<PosLoyaltyPoints> points = en.createNamedQuery("posLoyaltyPoints.findAll", PosLoyaltyPoints.class).getResultList();
        for(final PosLoyaltyPoints point : points){
            if((posSale.getLoyaltyCard().getEarnedPoints() >= point.getLpPoints()) 
                    && (posSale.getLoyaltyCard().getEarnedPoints() - pointsToRemove < point.getLpPoints())){
                rewardedValue -= point.getLpValue();
            }
        }

        //Now update the rewards.
        PosLoyaltyCard card = posSale.getLoyaltyCard();
        card.setEarnedPoints(card.getEarnedPoints() - pointsToRemove);
        int currentCount = card.getGrowlerCount();
        while(currentCount > card.getGrowlerCount() - growlerPointsToRemove){
            currentCount--;
            if((currentCount % setup.getGrowlerCount()) == 0){
                freeGrowlerNumToRemove++;
            }
        }
        card.setGrowlerCount(card.getGrowlerCount() - growlerPointsToRemove);
        int newGrowlerNum = card.getEarnedGrowlers() + freeGrowlerNumToAdd - freeGrowlerNumToRemove;
        if(newGrowlerNum < 0){
            newGrowlerNum = 0;
        }
        card.setEarnedGrowlers(newGrowlerNum);
        saleMoney = saleMoney - posSale.getLoyaltyMoney();
        if(saleMoney < 0){
            saleMoney = 0;    
        }
        float earnedPctMoney = saleMoney * (setup.getValuePerPoint()/100);
        float newBalance = rewardedValue + posSale.getLoyaltyMoney() - earnedPctMoney;
        card.setCurrentBalance(newBalance);
        en.merge(card);
        posSale.setLoyaltyCard(card);
        
        return posSale;
    }
    
    /***************************************************************************
     * Database access Functions.
     **************************************************************************/
    private POSSale getPOSSaleFromID(final EntityManager en, final int posSaleId){
        TypedQuery<POSSale> query = en.createQuery(
                "SELECT p FROM POSSale p WHERE p.id = ?1", POSSale.class);
        query.setParameter(1, posSaleId);
        POSSale posSale = query.getSingleResult();
        return posSale;
    }
    
    private POSSetup getPOSSetup(final EntityManager en){
        TypedQuery<POSSetup> query = en.createQuery(
                "SELECT p FROM POSSetup p WHERE p.id = 1", POSSetup.class);
        POSSetup setup = query.getSingleResult();
        return setup;
    }
}
